"""
应用发送消息的实现
"""

from cptools import INFO
from _sender import MsgSender
from typing import Dict,Optional,List
import hashlib
from config import OFFICIAL_APIS
from consts import ACCESS_TOKEN_FILE_NAME,BASE_API_URL
from utils import is_file, is_image, is_video, is_voice, list_to_Str
from pathlib import Path
import json
from token_manager import TokenManager
from consts import MediaType

CUR_FILE = Path(__file__)
TOKEN_PATH = CUR_FILE.parent.joinpath(ACCESS_TOKEN_FILE_NAME)


class AppSender(MsgSender):
    """
    应用，支持多种消息类型的发送

    Attributes:
        corpId: 企业key
        corpSecret: 应用密钥
        agentId: 应用id
    """
    corpId: str
    corpSecret: str
    agentId: str
    accessToken: str
    msgSendApi: str

    def __init__(self, corpId: str = '', corpSecret: str = '', agentId: str = '', log_level: int = 20, *args, **kwargs):
        super().__init__(log_level, *args, **kwargs)
        confKeyMap = self._get_corp_conf_key(corpId, corpSecret, agentId)
        self.corpId = confKeyMap.get('corpId', '')
        self.corpSecret = confKeyMap.get('corpSecret', '')
        self.agentId = confKeyMap.get('agentId', '')
        self.__freshAccessToken()

    def __freshAccessToken(self):
        """
            更新应用的访问token
        """
        self.accessToken = TokenManager(corpid=self.corpId, agentid=self.agentId,
                                        corpsecret=self.corpSecret, proxies=self.proxies).get_assess_token()
        self.msgSendApi = BASE_API_URL.format(
            OFFICIAL_APIS.get('MESSAGE_SEND').format(self.accessToken))

    def _get_corp_conf_key(self, corpId: str = '', corpSecret: str = '', agentId: str = '', **kwargs) -> Dict[str, str]:
        """
            获取key的值从配置文件中
        """
        if corpId and corpSecret and agentId:
            return {
                "corpId": corpId,
                "corpSecret": corpSecret,
                "agentId": agentId
            }
        else:
            res = {}
            options = [
                 "corpId",
                "corpSecret",
                "agentId"
            ]
            for k, v in zip(options, self._get_local_key(section='', options=options)):
                res.update(k, v)
            return res

    def send_text(self, content: str, **kwargs):
        """
        发送文本消息
        """
        if not content:
            self.logger.error(self.errmsgs['text_error'])
            return {
                'errcode': 404,
                'errmsg': self.errmsgs['text_error']
            }

        data = {
            "text": {
                "content": content
            }
        }

        self._send(msg_type='text', data=data, **kwargs)

    def send_file(self, filePath: str, **kwargs):
        """
        应用发送文件
        """
        if not is_file(filePath):
            self.logger.error(self.errmsgs['file_error'])
            return {
                'errcode': 404,
                'errmsg': self.errmsgs['file_error']
            }

        return self._send(msg_type='file', media_path=filePath, **kwargs)

    def send_video(self, videoPath: str, **kwargs):
        """
        应用发送文件
        """
        if not is_video(videoPath):
            self.logger.error(self.errmsgs['video_error'])
            return {
                'errcode': 404,
                'errmsg': self.errmsgs['video_error']
            }
        
        return self._send(msg_type='file',media_path=videoPath,**kwargs)
    

    def send_voice(self, voicePath: str, **kwargs):
        """
        应用发送文件
        """
        if not is_voice(voicePath):
            self.logger.error(self.errmsgs['voice_error'])
            return {
                'errcode': 404,
                'errmsg': self.errmsgs['voice_error']
            }
        
        return self._send(msg_type='file',media_path=voicePath,**kwargs)
    

    def send_image(self,
                   image_path: str,
                   **kwargs):
        """
        发送图片，支持jpg、png、bmp
        :param image_path: 图片存储路径
        :return:
        """
        if not is_image(image_path):
            self.logger.error(self.errmsgs['image_error'])
            return {
                'errcode': 404,
                'errmsg': self.errmsgs['image_error']
            }
        return self._send(msg_type='image', media_path=image_path, **kwargs)


    def send_news(self,
                  title: str,
                  desp: Optional[str],
                  url: str,
                  picurl: Optional[str],
                  **kwargs):
        """
        发送图文消息
        :param title: 图文标题，不超过128个字节，超过会自动截断
        :param desp: 图文描述，可选，不超过512个字节，超过会自动截断
        :param url: 跳转链接
        :param picurl: 图片url，支持JPG、PNG格式，较好的效果为大图 1068*455，小图150*150。
        :return:
        """
        if not (title and url):
            self.logger.error(self.errmsgs['news_error'])
            return {
                'errcode': 404,
                'errmsg': self.errmsgs['news_error']
            }
        data = {
            "news": {
                "articles": [
                    {
                        "title": title,
                        "description": desp,
                        "url": url,
                        "picurl": picurl
                    }
                ]
            },
        }
        return self._send(msg_type='news', data=data, **kwargs)
    

    def send_mpnews(self,
                    title: str,
                    image_path: str,
                    content: str,
                    author: Optional[str],
                    content_source_url: Optional[str],
                    digest: Optional[str],
                    **kwargs):
        """
        发送mpnews消息
        :param title: 图文标题
        :param image_path: 缩略图所在路径
        :param content: 图文消息内容
        :param author: 作者信息
        :param content_source_url: 点击跳转链接
        :param digest: 图文消息描述
        :param kwargs:
        :return:
        """
        if not (title and image_path and content):
            self.logger.error(self.errmsgs['mpnews_error'])
            return {
                'errcode': 404,
                'errmsg': self.errmsgs['mpnews_error']
            }
        data = {
            "mpnews": {
                "articles": [
                    {
                        "title": title,
                        "author": author,
                        "content_source_url": content_source_url,
                        "content": content,
                        "digest": digest
                    }
                ]
            }}
        return self._send(msg_type='mpnews', data=data, media_path=image_path, **kwargs)
    

    def send_markdown(self,
                      content: str,
                      **kwargs):
        """
        发送markdown消息
        :param content: markdown文本数据或markdown文件路径
        :return:
        """
        if not content:
            self.logger.error(self.errmsgs['markdown_error'])
            return {
                'errcode': 404,
                'errmsg': self.errmsgs['markdown_error']
            }
        if content.endswith('.md'):
            try:
                content = Path(content).read_text()
            except (OSError, FileNotFoundError) as e:
                self.logger.warning('你可能正在尝试发送一个markdown文件，但文件并不存在或文件名过长，将直接发送内容.')
        data = {
            "markdown": {
                "content": content,
            },
        }
        return self._send(msg_type='markdown', data=data, **kwargs)
    

    def send_card(self,
                  title: str,
                  desp: str,
                  url: str,
                  btntxt: Optional[str],
                  **kwargs):
        """
        发送卡片消息
        :param title: 标题，不超过128个字节，超过会自动截断
        :param desp: 描述，不超过512个字节，超过会自动截断
        :param url: 点击后跳转的链接
        :param btntxt: 按钮文字。 默认为"详情"， 不超过4个文字，超过自动截断
        :return:
        """
        if not (title and desp and url):
            self.logger.error(self.errmsgs['card_error'])
            return {
                'errcode': 404,
                'errmsg': self.errmsgs['card_error']
            }
        data = {
            "textcard": {
                "title": title,
                "description": desp,
                "url": url,
                "btntxt": btntxt
            },
        }
        return self._send(msg_type='textcard', data=data, **kwargs)
    

    def send_taskcard(self,
                      title: str,
                      desp: Optional[str],
                      url: str,
                      task_id: str,
                      btn: List[Dict],
                      **kwargs):
        """
        发送任务发片消息
        :param title: 标题，不超过128个字节，超过会自动截断（支持id转译）
        :param desp: 描述，不超过512个字节，超过会自动截断（支持id转译）
        :param url: 点击后跳转的链接。最长2048字节，请确保包含了协议头(http/https)，需要先自行配置回调服务
        :param task_id: 任务id，同一个应用发送的任务卡片消息的任务id不能重复，只能由数字、字母和"_-@"组成，最长支持128字节
        :param btn: 按钮列表，按钮个数为1~2个，btn的例子如下，各个具体参数的含义参考：https://open.work.weixin.qq.com/api/doc/90000/90135/90236#任务卡片消息
        "btn":[
            {
                "key": "key111",
                "name": "批准",
                "color":"red",
                "is_bold": true
            },
            {
                "key": "key222",
                "name": "驳回"
            }
        ]
        :param kwargs: 其他的通用参数，在此统一处理
        -------接收方的数据----
        {
           'ToUserName': 企业号,
           'FromUserName': 发送消息的用户名
           'MsgType': 'event',
           'CreateTime': '1624762869',
           'EventKey': 事件key，服务端根据key值确定用户点击的情况
           'TaskId': 任务id，这个必须是唯一的，在同一应用中发过之后不能重复(建议通过时间戳hash结合的方式生成)
           'Agentid': 应用id
        }
        :return:
        """
        if not (title and task_id and btn):
            self.logger.error(self.errmsgs['taskcard_error'])
            return {
                'errcode': 404,
                'errmsg': self.errmsgs['taskcard_error']
            }
        data = {
            "interactive_taskcard": {
                "title": title,
                "description": desp,
                "url": url,
                "task_id": task_id,
                "btn": btn,
            },
        }
        return self._send(msg_type='interactive_taskcard', data=data, **kwargs)
    

    def create_chat(self,
                    users: list,
                    name: Optional[str] = '',
                    owner: Optional[str] = '',
                    chatid: Optional[str] = '',
                    show_chat: Optional[bool] = True):
        """
        创建应用群聊
        :param users: 用户id列表，至少2人
        :param name: 群聊名称
        :param owner: 群主id，不指定会随机
        :param chatid:
        :param show_chat: 群聊创建成功后是否发送一条消息让群聊在列表中显示出来（默认发送）
        :return:
        """
        if len(users) < 2:
            self.logger.error(self.errmsgs['create_chat_error'])
            return {
                'errcode': 404,
                'errmsg': self.errmsgs['create_chat_error']
            }
        data = {
            "name": name,
            "owner": owner,
            "userlist": users,
            "chatid": chatid,
        }
        res = self._post(url=self.appchat_create_api, data=data)
        if show_chat and res['errcode'] == 0:
            self.logger.info('群聊创建成功，发送第一条消息')
            self.send_text(content=f"群聊已创建，本群聊id为：{res['chatid']}", chatid=res['chatid'])
            return res
        else:
            return res
        

    def _send(self,msg_type:str,data:Dict = {},media_path: Optional[str]='', **kwargs):
        """
        新的统一内部发送接口，供不同消息推送接口调用
        :param msg_type:
        :param data:
        :param media_path: 只有需要media id的时候才传入
        :param kwargs:
        :return:
        """
        if not (kwargs.get('touser') or kwargs.get('toparty') or kwargs.get('totag')):
            # 三者均为空，默认发送全体成员
            kwargs['touser'] = ['@all']
        data['chatid'] = kwargs.get('chatid','')
        if not data['chatid']:
            # 不是发送群聊的消息
            data.update({
                "touser": list_to_Str(kwargs.get('touser', [])),
                "toparty": list_to_Str(kwargs.get('toparty',[])),
                "totag":list_to_Str(kwargs.get('totag',[])),
                "agentid":self.agentId,
                "enable_id_trans": kwargs.get('enable_id_trans'),
                "enable_duplicate_check": kwargs.get('enable_duplicate_check'),
                "duplicate_check_interval": kwargs.get('duplicate_check_interval')
            })
        data.update({
            "msgtype": msg_type,  # 注意这里msg_type已经是正确了，后面的改动不影响
            "safe": kwargs.get('safe', 0)
        })
        # 检查消息类型是否需要获取media_id, 如需要，则获s取，并做相应检查
        send_res = self.__check_type_and_send(msg_type, data, media_path)
        if send_res.get('errcode') == 0:
            return send_res
        elif send_res.get('errcode') == 40014 or send_res.get('errcode') == 42001:
            # invalid access token or token expired, refresh token
            self.logger.info("尝试重新获取token并发送消息")
            self.__fresh_msg_send_api()
            return self.__check_type_and_send(msg_type, data, media_path)
        else:
            self.logger.error(f"发送失败! 原因：{send_res['errmsg']}")
            return send_res
    

    def __check_type_and_send(self,msg_type:str,data:Dict={},media_path:str=''):
        """
        判断类型并发送消息
        """
        if msg_type in MediaType.get_media_types():
            # 发送媒体消息
            self._media_api = self.base_url.format(OFFICIAL_APIS['MEDIA_UPLOAD'].format(self.access_token, msg_type))
            media_res = self._get_media_id(media_type=msg_type, p_media=Path(media_path))
            data[msg_type] = {
                "media_id": media_res.get('media_id', '')
            }
        elif msg_type == 'mpnews':
            # mpnews比较特殊，单独处理
            self._media_api = self.base_url.format(OFFICIAL_APIS['MEDIA_UPLOAD'].format(self.access_token, MediaType.IMAGE.value))
            media_res = self._get_media_id(media_type=MediaType.IMAGE.value, p_media=Path(media_path))
            data[msg_type]["articles"][0]["thumb_media_id"] = media_res.get('media_id', None)
        if data['chatid']:
            return self._post(self.appchat_send_api, data)
        return self._post(self.msgSendApi, data)